﻿using System;
using Google.Protobuf;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;

using Google.Protobuf;
using Google.Protobuf.Collections;
using Google.Protobuf.Reflection;
using System.Collections.Generic;
using System.Diagnostics;
using Google.Protobuf.WellKnownTypes;
using Type = System.Type;

/// <summary>
/// protobuf 3.x 版本序列化生成
/// </summary>
public static partial class SerializeHelper
{
    //[MenuItem("Data/生成BattleData序列化")]
    //static void GenerateProtobuf3()
    //{
    //    string preText = "";
    //    preText += "using Google.Protobuf;\n";
    //    preText += "using Google.Protobuf.Collections;\n";
    //    preText += "using Google.Protobuf.Reflection;\n";
    //    preText += "using System.Collections.Generic;\n";

    //    GenerateProtobuf(3, preText, "","BattleData", battleScriptsDirectory, protoOutputPath,cSharpTempOutputPath);
    //}
    [MenuItem("Data/生成序列化NetMessage")]
    static void GenerateProtoNetMessage()
    {
        string preText = "";
        preText += "using Google.Protobuf;\n";
        preText += "using Google.Protobuf.Collections;\n";
        preText += "using Google.Protobuf.Reflection;\n";
        preText += "using System.Collections.Generic;\n";

        GenerateProtobuf(3, preText, "MessagePacket","BattleData",scriptsDirectory,protoOutputPath,cSharpTempOutputPath);
    }

    [MenuItem("Data/生成序列化BattleCoreData_Data")]
    static void GenerateProtoBattleCoreData_Data()
    {
        string preText = "";
        preText += "using Google.Protobuf;\n";
        preText += "using Google.Protobuf.Collections;\n";
        preText += "using Google.Protobuf.Reflection;\n";
        preText += "using System.Collections.Generic;\n";

        GenerateProtobuf(3, preText, "BattleCoreData","BattleData",scriptsDirectory,protoOutputPath,cSharpTempOutputPath);
    }

    /// <summary>
    /// 生成proto3序列化文件
    /// </summary>
    static string GenerateProto3(string protoOutputPath,Type type)
    {
        GenerateProto(protoOutputPath,type);


        StringBuilder protoText = new StringBuilder();

        protoText.Append(GenerateProto3SharpSerialize(type));
        //GameDebug.Log($"生成:{protoText}");

        return protoText.ToString();
    }


    [MenuItem("Data/TestProto2Charp")]
    static void TestProto2Sharp()
    {
        Proto2Sharp(scriptsDirectory,protoOutputPath,cSharpTempOutputPath);
    }

    /// <summary>
    /// .proto -> .cs
    /// </summary>
    /// <param name="cSharpTempOutputPath"></param>
    static void Proto2Sharp(string scriptsDirectory,string protoOutputPath, string cSharpTempOutputPath)
    {
        if (!Directory.Exists(cSharpTempOutputPath))
            Directory.CreateDirectory(cSharpTempOutputPath);

        if (!Directory.Exists(protoOutputPath))
            Directory.CreateDirectory(protoOutputPath);
        
        DirClearFiles(cSharpTempOutputPath);
        
        string[] protos = Directory.GetFiles(protoOutputPath);
        const int maxOnce = 40;
        int curIndex = 0;
        int times = Mathf.CeilToInt(protos.Length / (float)maxOnce);

        string protoDir = protoOutputPath.Replace(protectPath + "/ProtoGen/", "");
        
        GameDebug.Log($"protos.Length:{protos.Length} 分{times}次执行  protoDir:{protoDir}  protectPath:{protectPath}  cSharpTempOutputPath:{cSharpTempOutputPath}");

        string protoexe = "C:\\Develop\\protoc-3.9.1-win64\\bin\\protoc.exe";

        for (int i=0;i< times;i++)
        {
            StringBuilder cmdText = new StringBuilder();
            string pan =  protectPath.Substring(0, 2);
            //cmdText.Append($"{pan}\n");
            cmdText.Append($"cd {protectPath}/ProtoGen\n");// cmdText.Append($"cd {protectPath.Substring(2)}\\ProtoGen\n");
            //cmdText.Append("protogen.exe -i:.\\FixedVec2.proto -o:.\\FixedVec2.cs");

            Encoding utf8 = Encoding.UTF8;

            for (int j=0;j < maxOnce; j++)
            {
                if(curIndex >= protos.Length)
                {
                    break;
                }
                var proto = protos[curIndex];
                string fileName = GetFileNameWithPath(proto, false);
                string cmd = protoexe + string.Format(" .\\{0}\\{01}.proto --csharp_out=:.\\Export\\\n", protoDir, fileName, fileName + "_Auto");
                //GameDebug.Log(cmd);
                cmdText.Append(cmd);

                curIndex++;
            }
    #if LOG
            GameDebug.Log($"cmdText :{cmdText.ToString()}");
    #endif
            var outputLog = RunCmd(cmdText.ToString());
    #if LOG
            GameDebug.Log(outputLog);
    #endif
        }
        if (!string.IsNullOrEmpty(cSharpTempOutputPath))
        {
            string[] tempFile = Directory.GetFiles(cSharpTempOutputPath);

            GameDebug.Log($"PostProcessProtoOutPutScript tempFile.Length:{tempFile.Length}");

            foreach (var file in tempFile)
                PostProcessProtoOutPutScript(file);
        }
        string outputPath = scriptsDirectory + "/ProtoSharp_Auto.cs" ;
        string content = "using pb = global::Google.Protobuf;\n";
        content += "using pbc = global::Google.Protobuf.Collections;\n";
        content += "using pbr = global::Google.Protobuf.Reflection;\n";
        content += "using scg = global::System.Collections.Generic;";

        if (!string.IsNullOrEmpty(cSharpTempOutputPath))
        {
            string[] tempFile = Directory.GetFiles(cSharpTempOutputPath);

            GameDebug.Log($"tempFile.Length:{tempFile.Length}");

            foreach (var file in tempFile)
            {
                string cur = File.ReadAllText(file);
                cur = cur.Replace("using pb = global::Google.Protobuf;", "");
                cur = cur.Replace("using pbc = global::Google.Protobuf.Collections;", "");
                cur = cur.Replace("using pbr = global::Google.Protobuf.Reflection;", "");
                cur = cur.Replace("using scg = global::System.Collections.Generic;", "");
                
                content += cur;
            }

            if (File.Exists(outputPath))
                File.Delete(outputPath);

            File.WriteAllText(outputPath, content);
            GameDebug.Log($"导出文件：{outputPath}");
        }
        if (Directory.Exists(cSharpTempOutputPath))
            Directory.Delete(cSharpTempOutputPath, true);

        AssetDatabase.Refresh();
    }


    static void PostProcessProtoOutPutScript(string filePath)
    {
#if LOG
        GameDebug.Log("处理去掉namespace filePath:" + filePath);
#endif
        string content = File.ReadAllText(filePath);
        if (content.Contains("namespace"))
        {
            int index = content.IndexOf("namespace");
            int i = index;
            int count = 0;
            int max = content.Length;
            while (i < max)
            {
                if (!content[i].Equals('{'))
                {
                    count++;
                }
                else
                {
                    break;
                }
                i++;
            }
            GameDebug.Log($"namespace 序号：{index}  到第一个花括号的计数:{count + 1}");
            content = content.Remove(index, count + 1);

            int last = content.LastIndexOf("}");
            content = content.Remove(last, 1);


            File.WriteAllText(filePath, content);

        }

    }

    /// <summary>
    /// 生成 .proto 文件
    /// </summary>
    /// <param name="type"></param>
    static void GenerateProto(string protoOutputPath,Type type)
    {
    
        Assembly bataData = type.Assembly;//Assembly.Load("BattleData");
        FieldInfo[] fields = GetSerilizedFields(type);

        StringBuilder protoText = new StringBuilder();

        protoText.Append("syntax = \"proto3\";\n\n");

        string newTypeName = type.FullName + "_Data";
        protoText.Append(string.Format("message {0}", newTypeName));

        protoText.Append("\n{\n");

        int index = 1;
        for (int i = 0; i < fields.Length; i++)
        {
            // GameDebug.Log($"field:{fields[i].Name} type:{fields[i].FieldType}");

            string FieldName = GetProtoPropertyName(fields[i].Name);

            string typeStr = GetFieldTypeProtoString(fields[i].FieldType);

            //是列表或Array
            if (CheckTypeListOrArray(fields[i].FieldType))
            {
                string innerTypeStr = GetListOrArrayInnerTypeString(fields[i].FieldType);
                //非基本类型
                if (!IsBaseType(innerTypeStr))
                {
                    Type innerType = bataData.GetType(innerTypeStr);

                    if (innerType == null)
                    {
                        innerType = GetType(innerTypeStr);
                    }

                    string countFieldName = GetProtoPropertyName(fields[i].Name + "_Count");
                    protoText.Append("\t" + $"int32 {countFieldName} = {index};" + "\n");

                    index++;

                    string indexesFieldName = GetProtoPropertyName(fields[i].Name + "_Indexes");
                    protoText.Append("\t" + $"repeated int32 {indexesFieldName} = {index};" + "\n");

                    index++;

                    //是CoreDataEntity
                    if (CheckTypeIsChildOfCoreDataEntity(innerType))
                    {
                        string entityIdsFieldName = GetProtoPropertyName(fields[i].Name + "_EntityIDs");
                        protoText.Append("\t" + $"repeated int32 {entityIdsFieldName} = {index};" + "\n");

                        index++;
                    }
                    else
                    {
                        if (IsSerializedBaseType(innerType))
                        {
                            string typesFieldName = GetProtoPropertyName(fields[i].Name + "_Types");
                            protoText.Append("\t" + $"repeated int32 {typesFieldName} = {index};" + "\n");

                            index++;
                        }
                        
                        protoText.Append("\t" + $"{typeStr} {FieldName} = {index};" + "\n");

                        index++;
                    }
                }
                else
                {
                    if (fields[i].FieldType == typeof(byte[]))
                        protoText.Append("\t" + $"{typeStr} {FieldName} = {index};" + "\n");
                    else
                    {
                        string innerProtoStr = GetFieldTypeProtoString(Type.GetType(innerTypeStr));
                        protoText.Append("\t" + $"repeated {innerProtoStr} {FieldName} = {index};" + "\n");
                    }

                    index++;
                }
            }
            else
            //不是列表,不是Array
            {
                //是CoreDataEntity
                if (CheckTypeIsChildOfCoreDataEntity(fields[i].FieldType))
                {
                    string entityIdFieldName = GetProtoPropertyName(fields[i].Name + "_EntityID");
                    protoText.Append("\t" + $"int32 {entityIdFieldName} = {index};" + "\n");

                    index++;
                }
                else
                {
                    if (IsSerializedBaseType(fields[i].FieldType))
                    {
                        string typeFieldName = GetProtoPropertyName(fields[i].Name + "_Type");
                        protoText.Append("\t" + $"int32 {typeFieldName} = {index};" + "\n");

                        index++;
                    }
                    protoText.Append("\t" + $"{typeStr} {FieldName} = {index};" + "\n");

                    index++;
                }
            }



        }
        protoText.Append("\n}");

        SaveProtoFile(protoOutputPath,newTypeName, protoText.ToString());
    }

    static string RunCmd(string cmd)
    {
        Process proc = new Process();
        proc.StartInfo.CreateNoWindow = true;
        proc.StartInfo.FileName = "cmd.exe";
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.RedirectStandardError = true;
        proc.StartInfo.RedirectStandardInput = true;
        proc.StartInfo.RedirectStandardOutput = true;
        proc.Start();
        GameDebug.Log("执行命令:\n" + cmd);
        proc.StandardInput.WriteLine(cmd);
        proc.StandardInput.WriteLine("exit");

        StreamReader reader = proc.StandardOutput;
        string output = "";

        while (!reader.EndOfStream)
        {
            string cur = reader.ReadLine();
            //GameDebug.Log("out str:" + cur);
            output += cur;

        }
        //proc.WaitForExit();
        proc.Close();
        return output;
    }

    static void SaveProtoFile(string protoOutputDir,string fileName, string content)
    {
        if (!Directory.Exists(protoOutputDir))
            Directory.CreateDirectory(protoOutputDir);

        string path = Path.Combine(protoOutputDir, fileName + ".proto");
        var bytes = Encoding.UTF8.GetBytes(content);
        File.WriteAllBytes(path, bytes);
    }
    
    static string GetProtoPropertyName(string s)
    {
        s = s.Replace("_", "");
        string upper = s.Substring(0, 1).ToUpper() + s.Substring(1);
        return upper;
    }
    /// <summary>
    /// 生成应用Proto3 的序列化反序列化方法
    /// </summary>
    static string GenerateProto3SharpSerialize(Type type)
    {
        FieldInfo[] fields = GetSerilizedFields(type);

        Assembly bataData = type.Assembly;// Assembly.Load("BattleData");

        StringBuilder sb = new StringBuilder();

        string dataTypeName = GetGenerateDataTypeName(type);

        sb.Append("\n\n");

        bool isStruct = type.IsValueType;

        bool isParentInheritedFromISerialize = CheckTypeIsChildOfISerializeOrISerializeStruct(type.BaseType);
        

        if (isStruct)
        {
            sb.Append($"public partial struct {type.Name} :ISerializeStruct \n");
        }
        else
        {
            sb.Append($"public partial class {type.Name} {(isParentInheritedFromISerialize ? "" : ": ISerialize")} \n");
        }

        sb.Append("{\n");
        
        if (!isStruct)
        {
            //if (!IsChildOfSerializedBaseType(type.BaseType)) //OptBase子类特殊处理
            bool hasNoParamConstructor = type.GetConstructor(new Type[0]) != null;
            if(!hasNoParamConstructor)
            {
                sb.Append(string.Format("\tpublic {0}():base() {{}}\n\n", type.Name));
            }
        }
        sb.Append($"\n\tpublic static {type.Name} NewEntity()\n");
        sb.Append("\t{\n");
        sb.Append($"\t\treturn new {type.Name}();\n");
        sb.Append("\t}\n");

        if (CheckTypeIsChildOfCoreDataEntity(type, false))
        {
            sb.Append($"\n\tpublic override int GetEntityTypeID()\n");
            sb.Append("\t{\n");
            sb.Append($"\t    return EntityType.{type.Name};\n");
            sb.Append("\t}\n");
        }

        GenerateReadMethod(type, ref sb);
        GenerateEntityRenfereceMethod(type, ref sb);
        GenerateWriteMethod(type, ref sb);
        

        sb.Append("}\n");

        return sb.ToString();
    }
    
    /// <summary>
    /// 生成反序列化方法
    /// </summary>
    /// <param name="type"></param>
    /// <param name="sb"></param>
    static void GenerateReadMethod(Type type,ref StringBuilder sb)
    {
        FieldInfo[] fields = GetSerilizedFields(type);
        Assembly bataData = type.Assembly;// Assembly.Load("BattleData");
        string dataTypeName = GetGenerateDataTypeName(type);
        bool isStruct = type.IsValueType;
        bool isParentInheritedFromISerialize = CheckTypeIsChildOfISerializeOrISerializeStruct(type.BaseType);
        
        ///反序列化
        sb.Append($"\n\tpublic {(isStruct ? "" : "override")} void Read(byte[] bytes)\n");
        sb.Append("\t{\n");

        sb.Append($"\t\tif(bytes == null) return;\n");

        sb.Append($"\t\t{dataTypeName} data = CommonFunctions.Deserialize<{dataTypeName}>(bytes);\n");

        if (!isStruct)
        {
            sb.Append($"\t\tif (null == data)\n");
            sb.Append("\t\t{\n");
            sb.Append($"\t\t    GameDebug.LogError(\"{dataTypeName} deserialize failed\");\n");
            sb.Append("\t\t}\n");
        }

        for (int i = 0; i < fields.Length; i++)
        {
            if (!IsBaseType(fields[i].FieldType))
            {
                if (CheckTypeListOrArray(fields[i].FieldType) && !IsBaseType(GetListOrArrayInnerTypeString(fields[i].FieldType)))
                {
                    ///是否是列表 而且内部元素不是基本类型
                    string innerTypeStr = GetListOrArrayInnerTypeString(fields[i].FieldType);
                    Type innerType = bataData.GetType(innerTypeStr);
                    
                    if (innerType == null)
                    {
                        innerType = GetType(innerTypeStr);
                    }
                    if (CheckTypeIsChildOfCoreDataEntity(innerType))
                        continue;

                    string countName = $"{GetProtoPropertyName(fields[i].Name+"_Count")}";
                    string indexesName = $"{GetProtoPropertyName(fields[i].Name+"_Indexes")}";
                    string entityIDsName = $"{GetProtoPropertyName(fields[i].Name+"_EntityIDs")}";
                    string typeIDsName = $"{GetProtoPropertyName(fields[i].Name+"_Types")}";

                    sb.Append($"\n\t\tint {countName} = data.{countName};\n");

                    if (CheckTypeList(fields[i].FieldType))
                    {
                        sb.Append($"\t\t{fields[i].Name} = new List<{innerTypeStr}>();\n\n");
                    }
                    else
                    {
                        sb.Append($"\t\t{fields[i].Name} = new {innerTypeStr}[{countName}];\n\n");
                    }

                    if (CheckTypeIsChildOfCoreDataEntity(innerType))
                    {
                        sb.Append($"\t\t\tList<int> {entityIDsName} = data.{entityIDsName}.AsList();\n");
                    }

                    else
                    {
                        if (IsSerializedBaseType(innerType))//OptBase特殊处理
                        {
                            sb.Append($"\t\t\tList<int> {typeIDsName} =  data.{typeIDsName}.AsList();\n");
                        }

                        sb.Append($"\t\tList<int> {indexesName} =  data.{indexesName}.AsList();\n");
                    }

                    sb.Append($"\t\tfor(int i=0;i<{countName};i++)\n");
                    sb.Append("\t\t{\n");

                    if (innerType != null)
                    {
                        bool innerTypeIsStruct = innerType.IsValueType;

                        if (CheckTypeIsChildOfCoreDataEntity(innerType))
                        {
                            sb.Append($"\t\t\tvar newObj = CoreDataEntityFactory.GetEntity<{innerType}>({entityIDsName}[i]) as {innerType};\n");
                        }
                        else
                        {
                            sb.Append($"\t\t\tbyte[] allBytes = data.{GetProtoPropertyName(fields[i].Name)}.ToByteArray();\n");
                            
                            sb.Append($"\t\t\tint curIndex = {indexesName}[i];\n");

                            sb.Append($"\t\t\tint curLength = ((i+1)<{indexesName}.Count? {indexesName}[i+1]:allBytes.Length) - curIndex;\n");

                            sb.Append("\t\t\tbyte[] ebytes = new byte[curLength];\n");

                            sb.Append($"\t\t\tSystem.Array.Copy(allBytes,curIndex,ebytes,0,curLength);\n");

                            if (!innerTypeIsStruct)
                            {
                                sb.Append($"\n\t\t\t{innerTypeStr} newObj = null;\n");

                                if (IsSerializedBaseType(innerType))//特殊处理
                                {
                                    sb.Append($"\t\t\tint curType = {typeIDsName}[i];\n");

                                    sb.Append($"\t\t\tnewObj = {innerType.Name}.ReadBytes(curType,ebytes);\n\n");
                                }
                                else
                                {
                                    if (CheckTypeList(fields[i].FieldType))
                                    {
                                        sb.Append($"\t\t\tif(i<{fields[i].Name}.Count && {fields[i].Name}[i] != null)\n");
                                    }
                                    else
                                    {
                                        sb.Append($"\t\t\tif(i<{fields[i].Name}.Length && {fields[i].Name}[i] != null)\n");
                                    }

                                    sb.Append($"\t\t\t\t newObj = {fields[i].Name}[i];\n");
                                    sb.Append($"\t\t\telse\n");
                                    sb.Append($"\t\t\t\t newObj = {innerTypeStr}.NewEntity();\n\n");

                                    sb.Append($"\t\t\tnewObj.Read(ebytes);\n\n");
                                }
                            }
                            else
                            {
                                sb.Append($"\n\t\t\t{innerTypeStr} newObj =  {innerTypeStr}.NewEntity();\n");

                                sb.Append($"\t\t\tnewObj.Read(ebytes);\n\n");
                            }
                        }
                    }
                    else
                    {
                        GameDebug.LogError($"innerType == null innerTypeStr:{innerTypeStr}");
                        sb.Append($"\t\t\tvar newObj = new {innerTypeStr}();\n");
                        sb.Append($"\t\t\tnewObj.Read(ebytes);\n\n");
                    }

                    if (CheckTypeList(fields[i].FieldType))
                        sb.Append($"\t\t\t{fields[i].Name}.Add(newObj);\n");
                    else
                        sb.Append($"\t\t\t{fields[i].Name}[i] = newObj;\n");

                    sb.Append("\t\t}\n\n");
                }
                else
                {
                    if (CheckTypeIsChildOfISerializeOrISerializeStruct(fields[i].FieldType))
                    {
#if LOG
                        GameDebug.Log($"{fields[i].Name}是ISerialize");
#endif
                        bool fieldIsStruct = fields[i].FieldType.IsValueType;
                        if (!fieldIsStruct)
                        {
                            if (IsSerializedBaseType(fields[i].FieldType))//OptBase 特殊处理
                            {
                                string typeIDName = $"{GetProtoPropertyName(fields[i].Name+"_Type")}";
                                sb.Append($"\t\tint optType = data.{typeIDName};\n");

                                sb.Append($"\t\tthis.{fields[i].Name} = {fields[i].FieldType.Name}.ReadBytes(optType,data.{GetProtoPropertyName(fields[i].Name)}.ToByteArray());\n");
                            }
                            else if (CheckTypeIsChildOfCoreDataEntity(fields[i].FieldType))//.BaseType.GetField("m_EntityId") != null)
                            {
                                continue;
                            }
                            else
                            {
                                sb.Append($"\t\tif(this.{fields[i].Name} == null)\n");
                                sb.Append($"\t\t\tthis.{fields[i].Name} = {GetListOrArrayInnerTypeString(fields[i].FieldType)}.NewEntity();\n\n");

                                sb.Append($"\t\tthis.{fields[i].Name}.Read(data.{GetProtoPropertyName(fields[i].Name)}.ToByteArray());\n\n");
                            }
                        }
                        else
                        {
                            sb.Append($"\t\tthis.{fields[i].Name}.Read(data.{GetProtoPropertyName(fields[i].Name)}.ToByteArray());\n");
                        }

                    }
                    else
                    {
                        if (fields[i].FieldType.Equals(typeof(byte[])))
                        {
                            sb.Append($"\t\tthis.{fields[i].Name} = data.{GetProtoPropertyName(fields[i].Name)}.ToByteArray();\n");
                        }
                        else
                        {
                            bool isList = CheckTypeList(fields[i].FieldType) ;
                            sb.Append($"\t\tthis.{fields[i].Name} = data.{GetProtoPropertyName(fields[i].Name)}.{(isList?"AsList": "AsArray")}();\n");
                        }
                    }
                }
            }
            else
            {
                sb.Append($"\t\tthis.{fields[i].Name} = data.{GetProtoPropertyName(fields[i].Name)};\n");
            }
        }

    }


    /// <summary>
    /// 生成实体引用获取方法
    /// </summary>
    /// <param name="type"></param>
    /// <param name="sb"></param>
    static void GenerateEntityRenfereceMethod(Type type, ref StringBuilder sb)
    {
        FieldInfo[] fields = GetSerilizedFields(type);
        Assembly bataData = Assembly.Load("LogicData");// Assembly.Load("BattleData");
        string dataTypeName = GetGenerateDataTypeName(type);

        bool isStruct = type.IsValueType;
        bool isParentInheritedFromISerialize = CheckTypeIsChildOfISerializeOrISerializeStruct(type.BaseType);


        bool fieldsContainsCoreDataEntity = false;

        if (!CheckTypeIsChildOfCoreDataEntity(type, false))
        {
            for (int i = 0; i < fields.Length; i++)
            {
                if (CheckTypeIsChildOfCoreDataEntity(fields[i].FieldType))
                {
                    fieldsContainsCoreDataEntity = true;
                    break;
                }
                if (CheckTypeListOrArray(fields[i].FieldType))
                {
                    string innerTypeStr = GetListOrArrayInnerTypeString(fields[i].FieldType);
                    Type innerType = bataData.GetType(innerTypeStr);
                    if (innerType != null && CheckTypeIsChildOfCoreDataEntity(innerType))
                    {
                        fieldsContainsCoreDataEntity = true;
                        break;
                    }
                }
            }
        }

        //if (!CheckTypeIsChildOfCoreDataEntity(type, false) && fieldsContainsCoreDataEntity)
        //{
        //    sb.Append("\n\t\tEntityReference(bytes);\n");
        //}

        sb.Append("\t}\n");

        ///生成实体引用读取
        if (CheckTypeIsChildOfCoreDataEntity(type, false))
        {
            sb.Append("\n\tpublic override void EntityReference(byte[] bytes,CoreEntityCollection entityCollection)\n");
        }
        else if (fieldsContainsCoreDataEntity)
        {
            sb.Append("\n\tpublic void EntityReference(byte[] bytes,CoreEntityCollection entityCollection)\n");
        }

        if (CheckTypeIsChildOfCoreDataEntity(type, false) || fieldsContainsCoreDataEntity)
        {
            sb.Append("\t{\n");

            sb.Append($"\t\tif(bytes == null) return;\n");

            sb.Append($"\t\t{dataTypeName} data = CommonFunctions.Deserialize<{dataTypeName}>(bytes);\n");

            for (int i = 0; i < fields.Length; i++)
            {
                if (!IsBaseType(fields[i].FieldType))
                {
                    if (CheckTypeListOrArray(fields[i].FieldType) && !IsBaseType(GetListOrArrayInnerTypeString(fields[i].FieldType)))
                    {
                        string innerTypeStr = GetListOrArrayInnerTypeString(fields[i].FieldType);
                        Type innerType = bataData.GetType(innerTypeStr);

                        if (CheckTypeIsChildOfCoreDataEntity(innerType))
                        {
                            string countName = $"{GetProtoPropertyName(fields[i].Name+"_Count")}";
                            string indexesName = $"{GetProtoPropertyName(fields[i].Name+"_Indexes")}";
                            string entityIDsName = $"{GetProtoPropertyName(fields[i].Name+"_EntityIDs")}";

                            sb.Append($"\n\t\tint {countName} = data.{countName};\n");

                            if (CheckTypeList(fields[i].FieldType))
                                sb.Append($"\t\t{fields[i].Name} = new List<{innerType}>();\n");
                            else
                                sb.Append($"\t\t{fields[i].Name} = new {innerType}[{countName}];\n");

                            sb.Append($"\t\tList<int> {entityIDsName} =  data.{entityIDsName}.AsList();\n");


                            sb.Append($"\t\tfor(int i=0;i<{countName};i++)\n");
                            sb.Append("\t\t{\n");

                            sb.Append($"\t\t\tvar newObj_{i} = entityCollection.GetEntity<{innerType}>({entityIDsName}[i]);\n");

                            if (CheckTypeList(fields[i].FieldType))
                                sb.Append($"\t\t\t{fields[i].Name}.Add(newObj_{i});\n");
                            else
                                sb.Append($"\t\t\t{fields[i].Name}[i] = newObj_{i};\n");

                            sb.Append("\t\t}\n");
                        }
                    }
                    else
                    {
                        if (CheckTypeIsChildOfCoreDataEntity(fields[i].FieldType))
                        {
                            string entityIDName = $"{GetProtoPropertyName(fields[i].Name+"_EntityID")}";
                            sb.Append($"\t\tint {entityIDName} = data.{entityIDName};\n");

                            sb.Append($"\t\tvar newObj_{i} = entityCollection.GetEntity<{fields[i].FieldType}>({entityIDName});\n");
                            sb.Append($"\t\tthis.{fields[i].Name} = newObj_{i};\n\n");
                        }
                    }
                }

            }

            sb.Append("\t}\n");
        }
    }

    static Type GetType(string typeStr)
    {
        var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
        
        GameDebug.Log($"try get type:{typeStr}  all assembly count:{allAssemblies.Length}");
        
        string [] dataAssembly = new string[]{"BattleData","LogicData","Serialize"};
        foreach (string  assemblyStr in dataAssembly)
        {
            Assembly assembly= Assembly.Load(assemblyStr);
            if (assembly != null)
            {
                var type = assembly.GetType(typeStr);
                if (type != null)
                {
                    GameDebug.Log($"find type{typeStr} in {assembly.GetName()}");
                    return type;
                }
            }
            else
            {
                GameDebug.LogError($"assembly null :{assemblyStr}");
            }
        }
        foreach (Assembly assembly in allAssemblies)
        {
            GameDebug.Log($"assembly:{assembly.GetName()}");

            var type = assembly.GetType(typeStr);
            if (type != null)
            {
                GameDebug.Log($"find type{typeStr} in {assembly.GetName()}");
                return type;
            }
        }
        GameDebug.LogError($"cannot find type:{typeStr}");

        return null;
    }

    /// <summary>
    /// 生成序列化方法
    /// </summary>
    /// <param name="type"></param>
    /// <param name="sb"></param>
    static void GenerateWriteMethod(Type type, ref StringBuilder sb)
    {
        FieldInfo[] fields = GetSerilizedFields(type);
        Assembly bataData = type.Assembly;// Assembly.Load("BattleData");
        string dataTypeName = GetGenerateDataTypeName(type);

        bool isStruct = type.IsValueType;
        bool isParentInheritedFromISerialize = CheckTypeIsChildOfISerializeOrISerializeStruct(type.BaseType);

        sb.Append($"\n\tpublic {(isStruct ? "" : "override")} byte[] Write()\n");
        sb.Append("\t{\n");

        sb.Append($"\t\t{dataTypeName} data = new {dataTypeName}();\n");

        for (int i = 0; i < fields.Length; i++)
        {
            string dataFieldName = GetProtoPropertyName(fields[i].Name);
            
            if (!IsBaseType(fields[i].FieldType))
            {
                if (CheckTypeListOrArray(fields[i].FieldType) && !IsBaseType(GetListOrArrayInnerTypeString(fields[i].FieldType)))
                {
                    string innerTypeStr = GetListOrArrayInnerTypeString(fields[i].FieldType);
                    var innerType = Type.GetType(innerTypeStr);

                    if (innerType == null)
                        innerType = bataData.GetType(innerTypeStr);
                    
                    if (innerType == null)
                    {
                        innerType = GetType(innerTypeStr);
                    }
                    bool innerIsStruct = false;
                    if (innerType != null)
                        innerIsStruct = innerType.IsValueType;
                    else
                        GameDebug.LogError($"innerType == null  innerTypeStr:{innerTypeStr}");

                    sb.Append("\n");


                    string countName = $"{GetProtoPropertyName(fields[i].Name + "_Count")}";
                    string indexesName = $"{GetProtoPropertyName(fields[i].Name + "_Indexes")}";
                    string entityIDsName = $"{GetProtoPropertyName(fields[i].Name+"_EntityIDs")}";

                    string curIndexName = $"{GetProtoPropertyName(fields[i].Name+"_CurIndex")}";
                    string bytesName = $"{GetProtoPropertyName(fields[i].Name+"_Bytes")}";

                    string typesName = $"{GetProtoPropertyName(fields[i].Name+"_Types")}";

                    sb.Append($"\t\tif({fields[i].Name}!= null)\n");

                    sb.Append($"\t\t\tdata.{countName} = this.{fields[i].Name}.{(CheckTypeList(fields[i].FieldType) ? "Count" : "Length")};\n");


                    if (CheckTypeIsChildOfCoreDataEntity(innerType))
                    {
                        sb.Append($"\t\tList<int>{entityIDsName} = new List<int>();\n");
                    }
                    else
                    {
                        if (IsSerializedBaseType(innerType))
                        {
                            sb.Append($"\t\tList<int> {typesName} = new List<int>();\n");
                        }
                        sb.Append($"\t\tint {curIndexName} = 0;\n");
                        sb.Append($"\t\tList<int>{indexesName} = new List<int>(){{0}};\n");
                        sb.Append($"\t\tList<byte> {bytesName} = new List<byte>();\n");
                    }

                    sb.Append($"\t\tfor(int i=0;i<data.{countName};i++)\n");

                    sb.Append("\t\t{\n");
                    
                    sb.Append($"\t\t\tbyte[] curBytes = new byte[0];\n");

                    if (CheckTypeIsChildOfCoreDataEntity(innerType))
                    {
                        sb.Append($"\t\t\tif(this.{fields[i].Name}[i] != null)\n ");
                        sb.Append($"\t\t\t    {entityIDsName}.Add({fields[i].Name}[i].entityId);\n");
                    }
                    else
                    {
                        if (IsSerializedBaseType(innerType))
                        {
                            sb.Append($"\t\t\t{typesName}.Add(this.{fields[i].Name}[i].type);\n");
                        }

                        if (!innerType.IsSubclassOf(typeof(ISerializeStruct)) && !innerType.IsValueType)
                        {
                            sb.Append($"\t\t\tif(this.{fields[i].Name}[i] != null)\n\t");
                        }
                        
                        sb.Append($"\t\t\tcurBytes = this.{fields[i].Name}[i].Write();\n");

                        sb.Append($"\t\t\t{curIndexName} += curBytes.Length;\n");

                        sb.Append($"\t\t\t{indexesName}.Add({curIndexName});\n");

                        sb.Append($"\t\t\t{bytesName}.AddRange(curBytes);\n");
                    }

                    sb.Append("\t\t}\n");

                    if (CheckTypeIsChildOfCoreDataEntity(innerType))
                    {
                        sb.Append($"\t\tdata.{entityIDsName}.ProtoListSetValue({entityIDsName});\n");
                    }
                    else
                    {
                        if (IsSerializedBaseType(innerType))
                        {
                            sb.Append($"\t\tdata.{typesName}.ProtoListSetValue({typesName});\n");
                        }

                        sb.Append($"\t\tdata.{indexesName}.ProtoListSetValue({indexesName});\n");
                        sb.Append($"\t\t\tdata.{dataFieldName} = ByteString.CopyFrom({bytesName}.ToArray());\n\n");
                    }
                }
                else
                {
                    if (CheckTypeListOrArray(fields[i].FieldType))
                    {
                        string typeStr = GetTypeString(fields[i].FieldType);

                        if (fields[i].FieldType.Equals(typeof(byte[])))
                        {
                            sb.Append($"\t\tif(this.{fields[i].Name}!= null)\n");
                            sb.Append($"\t\t\tdata.{dataFieldName} = ByteString.CopyFrom(this.{fields[i].Name});\n\n");
                        }
                        else
                        {
                            sb.Append($"\n\t\tforeach (var e in this.{fields[i].Name})\n");

                            sb.Append($"\t\t\tdata.{dataFieldName}.Add(e);\n\n");
                        }
                    }
                    else
                    {
                        bool fieldIsStruct = fields[i].FieldType.IsValueType;

                        if (CheckTypeIsChildOfISerializeOrISerializeStruct(fields[i].FieldType))
                        {
                            string entityIDName = $"{GetProtoPropertyName(fields[i].Name + "_EntityID")}";
                            if (CheckTypeIsChildOfCoreDataEntity(fields[i].FieldType))
                            {
                                sb.Append($"\t\tdata.{entityIDName} = this.{fields[i].Name} == null? -1 : this.{fields[i].Name}.entityId;\n");
                            }
                            else if (IsSerializedBaseType(fields[i].FieldType))
                            {
                                string typeName = $"{GetProtoPropertyName(fields[i].Name + "_Type")}";
                                sb.Append($"\t\tdata.{typeName} =  this.{fields[i].Name}.type;\n");
                                sb.Append($"\n\t\tif(this.{fields[i].Name} != null)\n");
                                sb.Append($"\t\t\tdata.{dataFieldName} = ByteString.CopyFrom({fields[i].FieldType.Name}.WriteBytes(this.{fields[i].Name}));\n");
                                sb.Append($"\t\telse\n\t\t\tGameDebug.LogError(\"this.{fields[i].Name} == null\");\n\n");
                            }
                            else
                            {
                                if (!fieldIsStruct)
                                    sb.Append($"\t\tif(this.{fields[i].Name} != null)\n\t");
                                sb.Append($"\t\tdata.{dataFieldName} = ByteString.CopyFrom(this.{fields[i].Name}.Write());\n");
                            }
                        }
                        else
                        {
                            if (IsSerializedBaseType(fields[i].FieldType))
                            {
                                sb.Append($"\t\tdata.{dataFieldName} =  ByteString.CopyFrom(this.{fields[i].Name});\n");
                            }
                            else
                            {
                                string typeStr = GetTypeString(fields[i].FieldType);

                                sb.Append($"\t\tdata.{dataFieldName} = CommonFunctions.Serialize<{typeStr}>(this.{fields[i].Name});\n");
                            }
                        }
                    }
                }
            }
            else
            {
                if(fields[i].FieldType.Equals(typeof(string)))
                    sb.Append($"\t\tif({fields[i].Name} != null)\n\t");

                sb.Append($"\t\tdata.{dataFieldName} = this.{fields[i].Name};\n");
            }
        }
        sb.Append($"\t\treturn CommonFunctions.Serialize(data);\n");

        sb.Append("\t}\n");
    }

}

